package com.hscm.boss.mvc.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hscm.boss.common.client.RedisClient;
import com.hscm.boss.common.consants.KeyConstant;
import com.hscm.boss.common.enums.ResponseCode;
import com.hscm.boss.common.exception.BossException;
import com.hscm.boss.common.utils.JwtUtil;
import com.hscm.boss.dao.entity.PmsMenu;
import com.hscm.boss.dao.entity.PmsMenuUser;
import com.hscm.boss.dao.entity.PmsOperator;
import com.hscm.boss.dao.mapper.PmsMenuMapper;
import com.hscm.boss.dao.mapper.PmsMenuUserMapper;
import com.hscm.boss.dao.mapper.PmsOperatorMapper;
import com.hscm.boss.mvc.model.dto.operator.LoginResponseDto;
import com.hscm.boss.mvc.model.from.operator.LoginFrom;
import com.hscm.boss.mvc.model.menus.MenuItem;
import com.hscm.boss.mvc.model.menus.SubMenu;
import com.hscm.boss.mvc.service.OperatorService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;


@Service
public class OperatorServiceImpl implements OperatorService {

    @Resource
    private PmsOperatorMapper operatorMapper;

    @Resource
    private RedisClient redisClient;

    @Resource
    private PmsMenuMapper pmsMenuMapper;


    private Object objectLock = new Object();  //因为service是单例的 所以这个objectLock也是也是唯一的

    @Override
    public LoginResponseDto OperatorLogin(LoginFrom loginFrom) {
        //在数据库登录名称的字段值是唯一的  根据名字获取盐值 来验证密码是否正确
        QueryWrapper<PmsOperator> wrapper = new QueryWrapper<>();
        wrapper.eq("login_name", loginFrom.getLoginName());
        PmsOperator pmsOperator = operatorMapper.selectOne(wrapper);

        if (pmsOperator == null) {//说明没有这个用户
            //枚举类型能够通枚举类.的方式获取枚举中的值
            throw new BossException(ResponseCode.OPERATOR_LOGIN_NAME_PWD);
        }

        //获取salt ， 进行密码的比对
        String salt = pmsOperator.getSalt();
        //用登录的密码和salt的值生成md5的值 和 数据库中的密码进行比对
        //这是提交的密码值
        String requestLoginPwd = DigestUtil.md5Hex(loginFrom.getLoginPwd() + salt);
        if (!requestLoginPwd.equals(pmsOperator.getLoginPwd())) {
            //密码错误
            throw new BossException(ResponseCode.OPERATOR_LOGIN_NAME_PWD_1);
        }


        //登录的名称和密码是正确的  生成JWT
        String jwt = JwtUtil.createJWT(UUID.randomUUID().toString(), 120);

        //pmsOperator.getId()登录用户的ID
        String key = KeyConstant.TOKEN_OPERATOR + pmsOperator.getId();
        Map<String, String> redisData = new HashMap<>();
        //存储JWT operatorID LoginTime 到Redis
        redisData.put("jwt", jwt);
        redisData.put("operatorID", String.valueOf(pmsOperator.getId()));
        redisData.put("LoginTime", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));


        Boolean saveToken = redisClient.setHashMap(key, redisData, 120, TimeUnit.MINUTES);

        if (!saveToken) {
            throw new BossException(ResponseCode.LOGIN_SAVE_TOKEN_FAIL);
        }


        //登录成功  jwt也成功 返回登录结果给前端
        return new LoginResponseDto(pmsOperator.getId(), pmsOperator.getShowName(), jwt);
    }

    @Override
    public List<SubMenu> getMenuByLoginUser(Long operatorId) {

        List<SubMenu> subMenuList = new ArrayList<>();
        //1.从redis获取数据
        String operatorMenusKey = KeyConstant.OPERATOR_MENUS + operatorId;
        //取数据
        String menuJSONArray = redisClient.getValue(operatorMenusKey);
        if (StrUtil.isNotBlank(menuJSONArray)) {//有redis数据
            //进行反序列化操作
            subMenuList = JSONUtil.toList(menuJSONArray, SubMenu.class);
        } else {//没有redis数据


            //在redis反序列化的时候可能会出现数据错误 ， 因此需要对可能出现的异常进行异常处理
            try {
                synchronized (objectLock) {
                    //取数据
                    menuJSONArray = redisClient.getValue(operatorMenusKey);
                    if (StrUtil.isNotBlank(menuJSONArray)) {//取出的数据不为空
                        subMenuList = JSONUtil.toList(menuJSONArray, SubMenu.class);
                    } else {
                        //从数据库中取值然后进行存储
                        subMenuList = getMenusDataFromDB(operatorId);
                        redisClient.setValue(operatorMenusKey, JSONUtil.toJsonStr(subMenuList));
                    }
                }
            } catch (Exception e) {
                //当有异常 就从数据库中取值然后进行存储
                subMenuList = getMenusDataFromDB(operatorId);
                e.getStackTrace();
            }

        }


        return subMenuList;
    }


    //退出功能
    @Override
    public void OperatorLogout(Long operatorId) {
        String  key = KeyConstant.TOKEN_OPERATOR + operatorId;
        Boolean del = redisClient.deleteKey(key);
        if( del == null || del == false){
            throw new BossException(ResponseCode.UNKNOWN);
        }
    }

    private List<SubMenu> getMenusDataFromDB(Long operatorId) {
        //从数据库中查询菜单数据
        List<PmsMenu> pmsMenuList = pmsMenuMapper.selectListByUserId(operatorId);

        //数据库中前几位相同的数据放在Map中 key 是数据库Number列中前四位  value 是前四位相同的list中
        String numberPrefix4 = "";
        HashMap<String, List<PmsMenu>> listMapMenu = new HashMap<>();

        //把数据库中查出来的数据装到Map中
        for (PmsMenu pmsMenu : pmsMenuList) {
            //获取数据库中 Number 字段的前四位
            numberPrefix4 = pmsMenu.getNumber().substring(0, 4);

            //这个Map中如果有Number字段中是否为前四的字符  就把当前的菜单放到Map集合这中的Value中
            if (listMapMenu.containsKey(numberPrefix4)) {
                listMapMenu.get(numberPrefix4).add(pmsMenu);
            } else {
                //这个Map中如果Number字段不是中前四的字符 就把当前的菜单放到新的list集合，然后把这个新的list放到Map中
                List<PmsMenu> itemList = new ArrayList<>();
                itemList.add(pmsMenu);
                listMapMenu.put(numberPrefix4, itemList);
            }
        }

        System.out.println(listMapMenu);


        //从Map中的查菜单 ， 对list 集合进行组装 ，达到list<SubMenu>
        List<SubMenu> subMenuList = new ArrayList<>();
        listMapMenu.forEach((number, menuList) -> {

            String leaf = "";
            SubMenu subMenu = new SubMenu();
            List<MenuItem> items = new ArrayList<>();

            for (PmsMenu pmsMenu : menuList) {
                leaf = pmsMenu.getIsLeaf();
                if ("NO".equals(leaf)) {
                    subMenu.setName(pmsMenu.getName());
                    subMenu.setUrl(pmsMenu.getUrl());
                    subMenu.setNumber(pmsMenu.getNumber());
                } else {
                    MenuItem menuItem = new MenuItem();
                    menuItem.setName(pmsMenu.getName());
                    menuItem.setUrl(pmsMenu.getUrl());
                    menuItem.setNumber(pmsMenu.getNumber());
                    items.add(menuItem);
                }
            }
            //给SubMenu的items 赋值
            subMenu.setItems(items);
            subMenuList.add(subMenu);
        });

        //对展示的结果进行排序
        return subMenuList.stream().sorted(Comparator.comparing(SubMenu::getNumber)).toList();
    }
}
